<?php
namespace juven\libs {
    \defined( '_JEXEC' ) or die( 'Restricted access' );

    /** @todo fingerprint or something else, for more security (eg.to prevent CSRF attacks, etc.) */
    class Session {
        
        /** @var DynamicContainer */
        protected $_vars = \NULL;
        
        /** @var DatabaseAdapter */
        protected $_db = \NULL;
        
        /** @var ConfigurationContainer */
        protected $_dbConnectionData = \NULL;
        
        /** @var String */
        protected $_dbTable = \NULL;
        
        /** @var array */
        protected $_session_record = array(); 

        public function __construct(DatabaseAdapter $dbAdapter, array $dbConnectionData, $dbTable="sessions") {
            $this->_vars            = new DynamicContainer();            
            $this->_vars->db        = new DynamicContainer();
            $this->_vars->db->table = $dbTable;
            $this->_vars->db->connectionData = new DynamicContainer($dbConnectionData->toArray());
            
            $this->_vars->events = new DynamicContainer();
            \session_start();
            $this->_db = $dbAdapter;
            $this->_dbConnectionData = $dbConnectionData;
            $this->_dbTable = $dbTable;
            $this->_session_record = $this->get_session_record_from_database(\session_id());
                
            if (!isset($this->_session_record["id"])){ // Session does not exists in database.
                $this->create_new_session_record_into_database(array(
                    'session_id'    =>\session_id(),
                    'user_id'       =>juven::config()->db->guest_user_id,
                    'date_created'  =>\date ("Y-m-d H:i:s", \time()),
                    'session_data'  =>(string)\json_encode(array("ftv"=>\TRUE)),
                    'status'        =>1, // 0=inactive, 1=active, 2=blocked
                ));
                $this->_session_record = $this->get_session_record_from_database(\session_id());
                $this->_session_record["session_data"] = new DynamicContainer((array)\json_decode($this->_session_record["session_data"],\TRUE,512));
            } else { // Session exists in database.
                $this->_session_record["session_data"] = new DynamicContainer((array)\json_decode($this->_session_record["session_data"],\TRUE,512));
                $this->_session_record["session_data"]->ftv = \FALSE;
                $this->save();
            }     
        }

        /**
         * Save/update session_data in the database
         * @return integer Returns the number of affected rows
         */
        public function save(){
            $dtp = new \stdClass();
            $dtp->dbConnectionData          = new DynamicContainer(juven::config()->db->toArray());
            $dtp->sessionData_toSave        = array('session_data'=>(string)\json_encode($this->_session_record["session_data"]->toArray()));
            $dtp->session_id                = $this->_session_record["session_id"];
            $dtp->saveSessionResults        = \NULL;
            $dtp->continue                  = \TRUE;

            juven::eventsManager()->publish(self::EVENT_onBefore_SaveSessionDataToDatabase, $dtp);
            if ($dtp->continue===\TRUE){
                $this->_db->connect($dtp->dbConnectionData);
                $dtp->saveSessionResults = $this->_db->update($this->_dbTable, $dtp->sessionData_toSave, "`session_id`='".$dtp->session_id."'"); 
                $this->_db->disconnect();
            }    
            juven::eventsManager()->publish(self::EVENT_onAfter_SaveSessionDataToDatabase, $dtp);
            return $dtp->saveSessionResults;
        }

        /**
         * It will try to totaly destroy the session....also it will regenerate the session_id
         * @return integer Returns the number of affected rows
         */
        public function delete() {
            $dtp                            = new \stdClass();
            $dtp->dbConnectionData          = new DynamicContainer(juven::config()->db->toArray());
            $dtp->deleteSessionResults      = \NULL;
            $dtp->continue                  = \TRUE;
            $dtp->session_id                = $this->_session_record["session_id"];

            juven::eventsManager()->publish(self::EVENT_onBefore_DeleteSessionRecordFromDatabase, $dtp);
            if ($dtp->continue===\TRUE){
                \session_write_close(); \session_destroy(); \session_unset(); \setcookie(\session_name(),'',0,'/'); 
                $_SESSION = array(); unset($_SESSION); \session_start(); \session_regenerate_id(\true);
                $this->_db->connect($dtp->dbConnectionData);
                $dtp->deleteSessionResults = $this->_db->delete_all($this->_dbTable, "`session_id`='".$dtp->session_id."'");
                $this->_db->disconnect();
            }    
            juven::eventsManager()->publish(self::EVENT_onAfter_DeleteSessionRecordFromDatabase, $dtp);
            return $dtp->deleteSessionResults;
        }

        /**
         * @param string $session_id 
         * @return array Returns Assoc array with session record data
         */
        protected function get_session_record_from_database($session_id){
            $dtp                            = new \stdClass();
            $dtp->dbConnectionData          = new DynamicContainer(juven::config()->db->toArray());
            $dtp->sqlQuery                  = "SELECT * FROM `".$this->_dbTable."` WHERE `session_id`=:s_id LIMIT 1";
            $dtp->bindParams                = array(':s_id'=> (string)$session_id);
            $dtp->sessionRecordFromDatabase = array();
            $dtp->continue                  = \TRUE;
            
            juven::eventsManager()->publish(self::EVENT_onBefore_GetSessionRecordFromDatabase, $dtp);
            if ($dtp->continue===\TRUE){
                $this->_db->connect($dtp->dbConnectionData);
                $select_session_results = $this->_db->select($dtp->sqlQuery, $dtp->bindParams);
                $this->_db->disconnect();
                if (\count($select_session_results)=='1'){ $dtp->sessionRecordFromDatabase = $select_session_results[0]; unset($select_session_results);}
            }    
            juven::eventsManager()->publish(self::EVENT_onAfter_GetSessionRecordFromDatabase, $dtp);
            return $dtp->sessionRecordFromDatabase;
        }

        /**
         * @param array $new_session_record_data 
         * @return integer Returns the id of the inserted row
         */
        protected function create_new_session_record_into_database(array $new_session_record_data){
            $dtp = new \stdClass();
            $dtp->dataToInsert = $new_session_record_data;
            juven::eventsManager()->publish(self::EVENT_onBefore_CreateNewSessionRecordIntoDatabase, $dtp);
                $this->_db->connect(juven::config()->db);
                $dtp->insert_results = $this->_db->insert($this->_dbTable, $dtp->dataToInsert);
                $this->_db->disconnect();
            juven::eventsManager()->publish(self::EVENT_onAfter_CreateNewSessionRecordIntoDatabase, $dtp);
            return $dtp->insert_results;
        }

        public function __get($name) {
            if (!array_key_exists($name, $this->_session_record)){ return \NULL; }
            return $this->_session_record[$name];
        }
        
        public function __isset($name) { return isset($this->_session_record[$name]); }
        
        public function EVENT_onBefore_SaveSession(){           return __CLASS__."\\".__FUNCTION__; }
        public function EVENT_onBefore_CreateNewSession(){      return __CLASS__."\\".__FUNCTION__; }
        public function EVENT_onBefore_DeleteSession(){         return __CLASS__."\\".__FUNCTION__; }
        public function EVENT_onBefore_GetSession(){            return __CLASS__."\\".__FUNCTION__; }
        public function EVENT_onAfter_SaveSession(){            return __CLASS__."\\".__FUNCTION__; }
        public function EVENT_onAfter_CreateNewSession(){       return __CLASS__."\\".__FUNCTION__; }
        public function EVENT_onAfter_DeleteSession(){          return __CLASS__."\\".__FUNCTION__; }
        public function EVENT_onAfter_GetSession(){             return __CLASS__."\\".__FUNCTION__; }
    }
}